#include "stdafx.h"
#include "PlayerObj.h"
#include "graphics/Shader.h"
#include "graphics/Primitives.hpp"
#include "terrain/Terrain.h"
#include "core/Game.h"
#include "core/Collision.hpp"

PlayerObj::PlayerObj(glm::vec3 pos, Player* owner) : Obj(pos), owner(owner), shader(new Shader()), dim(1.0f), renderOptions(0), 
	color(0.3f, 0.5f, 1.0f, 1.0f), v(0.0f, 0.0f), maxV(15.0f), m(5.0f), accl(10.0f), f(0.0f, 0.0f), sphereGeom(pos, dim), health(100.0f)
{
	weapons.push_back( new Weapon(owner, pos, glm::vec3(0.0f, dim/2.0f, 0.0f), glm::vec3(0.5f, 0.1f, 0.5f)) );
	/*weapons.push_back( new Weapon(owner, pos, glm::vec3(0.0f, 3.0f + dim/2.0f, 0.0f), glm::vec3(0.5f, 0.1f, 0.5f)) );
	weapons.push_back( new Weapon(owner, pos, glm::vec3(0.0f, 4.0f + dim/2.0f, 0.0f), glm::vec3(0.5f, 0.1f, 0.5f)) );
	weapons.push_back( new Weapon(owner, pos, glm::vec3(0.0f, 6.0f + dim/2.0f, 0.0f), glm::vec3(0.5f, 0.1f, 0.5f)) );
	weapons.push_back( new Weapon(owner, pos, glm::vec3(0.0f, 7.0f + dim/2.0f, 0.0f), glm::vec3(0.5f, 0.1f, 0.5f)) );
	weapons.push_back( new Weapon(owner, pos, glm::vec3(0.0f, 8.0f + dim/2.0f, 0.0f), glm::vec3(0.5f, 0.1f, 0.5f)) );*/
	/*weapons.push_back( new Weapon(owner, pos, glm::vec3(0.5f, 5.1f, 0.5f)) );*/
	type = type_playerObj;
}
PlayerObj::~PlayerObj(){}

void PlayerObj::update(float dt)
{
	float height = Terrain::getInstance()->sample(glm::vec2(m_pos.x, m_pos.z));
	m_pos.y = height + dim*0.5f;

	glm::vec2 currentPos = glm::vec2(m_pos.x, m_pos.z);

	glm::vec2 newPos;

	if (pather.enabled) {
		glm::vec2 dir = (pather.dest - currentPos);
		glm::vec2 dirN = glm::normalize(dir);

		glm::vec2 desiredV = dirN * maxV;
		glm::vec2 steeringForce = desiredV - v;
		f += steeringForce;
	} 
	else {
		float vLength = glm::length(v);
		if (vLength > 1e-4) {
			glm::vec2 vNorm = glm::normalize(v);
			float breakForce = 1.0f/* * dt */* vLength; // make force proportional to slow down as we approach target 
			f = -vNorm * breakForce;
		}
	}
	
	v += f * accl * dt;
	f = glm::vec2(0.0f, 0.0f);
	if (glm::length(v) > maxV)
		v = glm::normalize(v)*maxV;
	
	newPos = currentPos + v * dt;	

	m_pos.x = newPos.x; 
	m_pos.z = newPos.y;

	/* Update all the weapons */ 
	for ( unsigned int i = 0; i < weapons.size(); ++i) {
		weapons[i]->move(m_pos);
	}
}

void PlayerObj::addForce(const glm::vec2& force) {
	f += force;
}

void PlayerObj::draw(bool picking)
{
	BoxPrimitive* boxRenderer = Game::getInstance()->boxRenderer;

	if (picking) {
		boxRenderer->draw(m_pos, glm::vec3(dim), glm::vec4(0.0f, 0.0f, static_cast<float>(m_pickColor)/0xffff, 0.0f));
	}
	else if ( (renderOptions & SELECTED) == SELECTED ) 
		boxRenderer->draw(m_pos, glm::vec3(dim), glm::vec4(0.0f, 1.0, 0.0f, 0.0f));
	else
		boxRenderer->draw(m_pos, glm::vec3(dim), color);
}

CollGeom* PlayerObj::getCollGeom() {
	sphereGeom.pos = m_pos;
	return &sphereGeom;
}

void PlayerObj::handleColl(Obj* other) {
	other->handleColl(this);
}

void PlayerObj::handleColl(PlayerObj* other) {
	// sphere sphere coll response 

	PlayerObj* s0 = this;
	PlayerObj* s1 = other;

	glm::vec3 v0 = glm::vec3(s0->v.x, 0.0f, s0->v.y);
	glm::vec3 v1 = glm::vec3(s1->v.x, 0.0f, s1->v.y);
	float m0 = s0->m;
	float m1 = s1->m;

	glm::vec3 dist = s1->m_pos - s0->m_pos;
	glm::vec3 x = glm::normalize(dist);

	float x0 = glm::dot(x, v0); 
	glm::vec3 v0x = x * x0; // projection of v0 onto collision vector
	glm::vec3 v0y = v0 - v0x; // non projected component 

	x *= -1.0f;
	float x1 = glm::dot(x, v1);
	glm::vec3 v1x = x * x1;
	glm::vec3 v1y = v1 - v1x;

	glm::vec3 v0After = v0x*( (m0-m1)/(m0+m1) ) + v1x*( (2.0f*m1)/(m0+m1) ) + v0y;
	glm::vec3 v1After = v1x*( (m1-m0)/(m1+m0) ) + v0x*( (2.0f*m0)/(m1+m0) ) + v1y;

	s0->v = glm::vec2(v0After.x, v0After.z);
	s1->v = glm::vec2(v1After.x, v1After.z);

	float seperationDist = s0->dim + s1->dim;
	float residue = seperationDist - glm::length(dist);
	// note x*=-1.0 so signs change below 
	s0->m_pos += x*residue*0.5f;
	s1->m_pos -= x*residue*0.5f;
}

void PlayerObj::handleColl(CannonBall* other) {
	other->handleColl(this);
}

void PlayerObj::attackMe(float damage) {
	health -= damage; 
	if (health < 0.0f) {
		toBeDeleted = true;
		for ( unsigned int i = 0; i < weapons.size(); ++i)
			weapons[i]->toBeDeleted = true;
	}
}